<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>什么是跨域？浏览器如何拦截响应？如何解决？ | yanyan</title>
    <meta name="description" content="Yan&#39;s blog">
    <meta name="generator" content="VuePress 1.4.0">
    <script src="https://cdn.bootcss.com/jquery/3.5.0/jquery.slim.min.js"></script>
  <script src="https://cdn.bootcss.com/fancybox/3.5.7/jquery.fancybox.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/fancybox/3.5.7/jquery.fancybox.min.css">
  <link rel="shortcut icon" type="image/x-icon" href="./favicon.ico">
    
    <link rel="preload" href="/assets/css/0.styles.e1b3f17d.css" as="style"><link rel="preload" href="/assets/js/app.e358a08d.js" as="script"><link rel="preload" href="/assets/js/2.88fa18d1.js" as="script"><link rel="preload" href="/assets/js/33.cac3e2f0.js" as="script"><link rel="prefetch" href="/assets/js/10.23baf844.js"><link rel="prefetch" href="/assets/js/11.45c148ba.js"><link rel="prefetch" href="/assets/js/12.e5930132.js"><link rel="prefetch" href="/assets/js/13.0547cd14.js"><link rel="prefetch" href="/assets/js/14.3e67795b.js"><link rel="prefetch" href="/assets/js/15.51129890.js"><link rel="prefetch" href="/assets/js/16.6987f89d.js"><link rel="prefetch" href="/assets/js/17.2807cff5.js"><link rel="prefetch" href="/assets/js/18.855e1707.js"><link rel="prefetch" href="/assets/js/19.6da24791.js"><link rel="prefetch" href="/assets/js/20.e24d4aef.js"><link rel="prefetch" href="/assets/js/21.6efc6fba.js"><link rel="prefetch" href="/assets/js/22.10447f0f.js"><link rel="prefetch" href="/assets/js/23.9154cc24.js"><link rel="prefetch" href="/assets/js/24.9ad529fc.js"><link rel="prefetch" href="/assets/js/25.4c092e0a.js"><link rel="prefetch" href="/assets/js/26.debdaa01.js"><link rel="prefetch" href="/assets/js/27.8b90b660.js"><link rel="prefetch" href="/assets/js/28.1a323e01.js"><link rel="prefetch" href="/assets/js/29.6f108fc9.js"><link rel="prefetch" href="/assets/js/3.7210d3aa.js"><link rel="prefetch" href="/assets/js/30.e7df1937.js"><link rel="prefetch" href="/assets/js/31.2cb3120f.js"><link rel="prefetch" href="/assets/js/32.eb64932c.js"><link rel="prefetch" href="/assets/js/34.19ea35c4.js"><link rel="prefetch" href="/assets/js/35.fadf5d03.js"><link rel="prefetch" href="/assets/js/36.88b681f1.js"><link rel="prefetch" href="/assets/js/37.2a799db9.js"><link rel="prefetch" href="/assets/js/38.2741a2bf.js"><link rel="prefetch" href="/assets/js/39.359ceb72.js"><link rel="prefetch" href="/assets/js/4.9e938666.js"><link rel="prefetch" href="/assets/js/40.56fd4a10.js"><link rel="prefetch" href="/assets/js/41.e72117ad.js"><link rel="prefetch" href="/assets/js/42.63a6e190.js"><link rel="prefetch" href="/assets/js/43.c8072421.js"><link rel="prefetch" href="/assets/js/44.84cd8367.js"><link rel="prefetch" href="/assets/js/45.0ac810b0.js"><link rel="prefetch" href="/assets/js/46.bb83ff34.js"><link rel="prefetch" href="/assets/js/47.a9333a81.js"><link rel="prefetch" href="/assets/js/48.526b5494.js"><link rel="prefetch" href="/assets/js/49.73b61cc6.js"><link rel="prefetch" href="/assets/js/5.88b252c7.js"><link rel="prefetch" href="/assets/js/50.f34ab799.js"><link rel="prefetch" href="/assets/js/51.d06a49d9.js"><link rel="prefetch" href="/assets/js/52.348d5482.js"><link rel="prefetch" href="/assets/js/6.0face56b.js"><link rel="prefetch" href="/assets/js/7.31eca58d.js"><link rel="prefetch" href="/assets/js/8.69e9ce95.js"><link rel="prefetch" href="/assets/js/9.f25df9e1.js">
    <link rel="stylesheet" href="/assets/css/0.styles.e1b3f17d.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">yanyan</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <!----></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><!---->  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0"><a href="/html/001" class="sidebar-heading clickable"><span>HTML</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/css/image" class="sidebar-heading clickable"><span>CSS</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/js/001" class="sidebar-heading clickable"><span>JS-基础</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/js-v8/001" class="sidebar-heading clickable"><span>JS-V8引擎原理 </span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/js-async/001" class="sidebar-heading clickable"><span>JS-异步I/O及异步编程</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/http/000" class="sidebar-heading clickable open"><span>HTTP</span> <span class="arrow down"></span></a> <ul class="sidebar-links sidebar-group-items"><li><a href="/http/001.html" class="sidebar-link">01 HTTP 的特点？HTTP 的缺点</a></li><li><a href="/http/002.html" class="sidebar-link">02 说一说从输入URL到页面呈现发生了什么？——网络篇</a></li><li><a href="/http/003.html" class="sidebar-link">03 说一说从输入URL到页面呈现发生了什么？——解析算法篇</a></li><li><a href="/http/004.html" class="sidebar-link">04 说一说从输入URL到页面呈现发生了什么？——渲染过程篇</a></li><li><a href="/http/005.html" class="sidebar-link">05 谈谈你对重绘和回流的理解</a></li><li><a href="/http/006.html" class="sidebar-link">06 XSS攻击</a></li><li><a href="/http/007.html" class="sidebar-link">07 CSRF攻击</a></li><li><a href="/http/008.html" class="sidebar-link">08 (传统RSA版本)HTTPS为什么让数据传输更安全</a></li><li><a href="/http/009.html" class="sidebar-link">09 Https TLS 1.2  1.3</a></li><li><a href="/http/010.html" class="sidebar-link">10 Cookie</a></li><li><a href="/http/011.html" class="active sidebar-link">11 什么是跨域？浏览器如何拦截响应？如何解决</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/http/011.html#什么是跨域" class="sidebar-link">什么是跨域</a></li><li class="sidebar-sub-header"><a href="/http/011.html#cors" class="sidebar-link">CORS</a></li><li class="sidebar-sub-header"><a href="/http/011.html#jsonp" class="sidebar-link">JSONP</a></li><li class="sidebar-sub-header"><a href="/http/011.html#nginx" class="sidebar-link">Nginx</a></li></ul></li><li><a href="/http/012.html" class="sidebar-link">12 Http2</a></li><li><a href="/http/013.html" class="sidebar-link">13 Http缓存</a></li></ul></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/extend/002" class="sidebar-heading clickable"><span>拓展阅读</span> <span class="arrow right"></span></a> <!----></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="什么是跨域？浏览器如何拦截响应？如何解决？"><a href="#什么是跨域？浏览器如何拦截响应？如何解决？" class="header-anchor">#</a> 什么是跨域？浏览器如何拦截响应？如何解决？</h1> <p>在前后端分离的开发模式中，经常会遇到跨域问题，即 Ajax 请求发出去了，服务器也成功响应了，前端就是拿不到这个响应。接下来我们就来好好讨论一下这个问题。</p> <h2 id="什么是跨域"><a href="#什么是跨域" class="header-anchor">#</a> 什么是跨域</h2> <p>回顾一下 URI 的组成:
<a data-fancybox="" title="result" href="/images/html-011.png"><img src="/images/html-011.png" alt="result"></a></p> <p>浏览器遵循同源政策(scheme(协议)、host(主机)和port(端口)都相同则为同源)。非同源站点有这样一些限制:</p> <ul><li>能读取和修改对方的 DOM</li> <li>不读访问对方的 Cookie、IndexDB 和 LocalStorage</li> <li>限制 XMLHttpRequest 请求。(后面的话题着重围绕这个)</li></ul> <p>当浏览器向目标 URI 发 Ajax 请求时，只要当前 URL 和目标 URL 不同源，则产生跨域，被称为跨域请求。
跨域请求的响应一般会被浏览器所拦截，注意，是被浏览器拦截，响应其实是成功到达客户端了。那这个拦截是如何发生呢？
首先要知道的是，浏览器是多进程的，以 Chrome 为例，进程组成如下：</p> <p><a data-fancybox="" title="result" href="/images/html-012.png"><img src="/images/html-012.png" alt="result"></a></p> <p>WebKit 渲染引擎和V8 引擎都在渲染进程当中</p> <p>当xhr.send被调用，即 Ajax 请求准备发送的时候，其实还只是在渲染进程的处理。为了防止黑客通过脚本触碰到系统资源，浏览器将每一个渲染进程装进了沙箱，并且为了防止 CPU 芯片一直存在的Spectre 和 Meltdown漏洞，采取了站点隔离的手段，给每一个不同的站点(一级域名不同)分配了沙箱，互不干扰。具体见<a href="https://www.youtube.com/watch?v=dBuykrdhK-A&amp;feature=emb_logo" target="_blank" rel="noopener noreferrer">YouTube上Chromium安全团队的演讲视频<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>。</p> <p>在沙箱当中的渲染进程是没有办法发送网络请求的，那怎么办？只能通过网络进程来发送。那这样就涉及到进程间通信(IPC，Inter Process Communication)了。接下来我们看看 chromium 当中进程间通信是如何完成的，在 chromium 源码中调用顺序如下:
<a data-fancybox="" title="result" href="/images/html-013.png"><img src="/images/html-013.png" alt="result"></a></p> <p>可能看了你会比较懵，如果想深入了解可以去看看 chromium 最新的源代码，IPC源码地址及Chromium IPC源码解析文章。
总的来说就是利用Unix Domain Socket套接字，配合事件驱动的高性能网络并发库libevent完成进程的 IPC 过程。
好，现在数据传递给了浏览器主进程，主进程接收到后，才真正地发出相应的网络请求。
在服务端处理完数据后，将响应返回，主进程检查到跨域，且没有cors(后面会详细说)响应头，将响应体全部丢掉，并不会发送给渲染进程。这就达到了拦截数据的目的。
接下来我们来说一说解决跨域问题的几种方案。</p> <h2 id="cors"><a href="#cors" class="header-anchor">#</a> CORS</h2> <p>CORS 其实是 W3C 的一个标准，全称是跨域资源共享。它需要浏览器和服务器的共同支持，具体来说，非 IE 和 IE10 以上支持CORS，服务器需要附加特定的响应头，后面具体拆解。不过在弄清楚 CORS 的原理之前，我们需要清楚两个概念: 简单请求和非简单请求。</p> <p>浏览器根据请求方法和请求头的特定字段，将请求做了一下分类，具体来说规则是这样，凡是满足下面条件的属于简单请求:</p> <ul><li>请求方法为 GET、POST 或者 HEAD</li> <li>请求头的取值范围: Accept、Accept-Language、Content-Language、Content-Type(只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)</li></ul> <p>浏览器画了这样一个圈，在这个圈里面的就是简单请求, 圈外面的就是非简单请求，然后针对这两种不同的请求进行不同的处理。</p> <h3 id="简单请求"><a href="#简单请求" class="header-anchor">#</a> 简单请求</h3> <p>请求发出去之前，浏览器做了什么？
它会自动在请求头当中，添加一个Origin字段，用来说明请求来自哪个源。服务器拿到请求之后，在回应时对应地添加Access-Control-Allow-Origin字段，如果Origin不在这个字段的范围中，那么浏览器就会将响应拦截。
因此，Access-Control-Allow-Origin字段是服务器用来决定浏览器是否拦截这个响应，这是必需的字段。与此同时，其它一些可选的功能性的字段，用来描述如果不会拦截，这些字段将会发挥各自的作用。
Access-Control-Allow-Credentials。这个字段是一个布尔值，表示是否允许发送 Cookie，对于跨域请求，浏览器对这个字段默认值设为 false，而如果需要拿到浏览器的 Cookie，需要添加这个响应头并设为true, 并且在前端也需要设置withCredentials属性:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">let</span> xhr <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
xhr<span class="token punctuation">.</span>withCredentials <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
</code></pre></div><p>Access-Control-Expose-Headers。这个字段是给 XMLHttpRequest 对象赋能，让它不仅可以拿到基本的 6 个响应头字段（包括Cache-Control、Content-Language、Content-Type、Expires、Last-Modified和Pragma）, 还能拿到这个字段声明的响应头字段。比如这样设置:</p> <div class="language- extra-class"><pre class="language-text"><code>Access-Control-Expose-Headers: aaa
</code></pre></div><p>那么在前端可以通过 XMLHttpRequest.getResponseHeader('aaa') 拿到 aaa 这个字段的值</p> <h3 id="非简单请求"><a href="#非简单请求" class="header-anchor">#</a> 非简单请求</h3> <p>非简单请求相对而言会有些不同，体现在两个方面: 预检请求和响应字段。</p> <p>我们以 PUT 方法为例。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">var</span> url <span class="token operator">=</span> <span class="token string">'http://xxx.com'</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> xhr <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
xhr<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">'PUT'</span><span class="token punctuation">,</span> url<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
xhr<span class="token punctuation">.</span><span class="token function">setRequestHeader</span><span class="token punctuation">(</span><span class="token string">'X-Custom-Header'</span><span class="token punctuation">,</span> <span class="token string">'xxx'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
xhr<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>当这段代码执行后，首先会发送预检请求。这个预检请求的请求行和请求体是下面这个格式:</p> <div class="language- extra-class"><pre class="language-text"><code>OPTIONS / HTTP/1.1
Origin: 当前地址
Host: xxx.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
</code></pre></div><p>预检请求的方法是OPTIONS，同时会加上Origin源地址和Host目标地址，这很简单。同时也会加上两个关键的字段:</p> <ul><li>Access-Control-Request-Method, 列出 CORS 请求用到哪个HTTP方法</li> <li>Access-Control-Request-Headers，指定 CORS 请求将要加上什么请求头</li></ul> <p>这是预检请求。接下来是响应字段，响应字段也分为两部分，一部分是对于预检请求的响应，一部分是对于 CORS 请求的响应。</p> <p>预检请求的响应。如下面的格式:</p> <div class="language- extra-class"><pre class="language-text"><code>HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
</code></pre></div><p>其中有这样几个关键的响应头字段:</p> <ul><li>Access-Control-Allow-Origin: 表示可以允许请求的源，可以填具体的源名，也可以填*表示允许任意源请求。</li> <li>Access-Control-Allow-Methods: 表示允许的请求方法列表。</li> <li>Access-Control-Allow-Credentials: 简单请求中已经介绍。</li> <li>Access-Control-Allow-Headers: 表示允许发送的请求头字段</li> <li>Access-Control-Max-Age: 预检请求的有效期，在此期间，不用发出另外一条预检请求。</li></ul> <p>在预检请求的响应返回后，如果请求不满足响应头的条件，则触发XMLHttpRequest的onerror方法，当然后面真正的CORS请求也不会发出去了。
CORS 请求的响应。绕了这么一大转，到了真正的 CORS 请求就容易多了，现在它和简单请求的情况是一样的。浏览器自动加上Origin字段，服务端响应头返回Access-Control-Allow-Origin。可以参考以上简单请求部分的内容。</p> <h2 id="jsonp"><a href="#jsonp" class="header-anchor">#</a> JSONP</h2> <p>虽然XMLHttpRequest对象遵循同源政策，但是script标签不一样，它可以通过 src 填上目标地址从而发出 GET 请求，实现跨域请求并拿到响应。这也就是 JSONP 的原理，接下来我们就来封装一个 JSONP:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">jsonp</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> url<span class="token punctuation">,</span> params<span class="token punctuation">,</span> callbackName <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token function-variable function">generateURL</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> dataStr <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span>
    <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">let</span> key <span class="token keyword">in</span> params<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      dataStr <span class="token operator">+=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>key<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>params<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&amp;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    dataStr <span class="token operator">+=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">callback=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>callbackName<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>url<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">?</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>dataStr<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve<span class="token punctuation">,</span> reject</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token comment">// 初始化回调函数名称</span>
    callbackName <span class="token operator">=</span> callbackName <span class="token operator">||</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>toString<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token string">','</span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
    <span class="token comment">// 创建 script 元素并加入到当前文档中</span>
    <span class="token keyword">let</span> scriptEle <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'script'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    scriptEle<span class="token punctuation">.</span>src <span class="token operator">=</span> <span class="token function">generateURL</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>scriptEle<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 绑定到 window 上，为了后面调用</span>
    window<span class="token punctuation">[</span>callbackName<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token function">resolve</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token comment">// script 执行完了，成为无用元素，需要清除</span>
      document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">removeChild</span><span class="token punctuation">(</span>scriptEle<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>当然在服务端也会有响应的操作, 以 express 为例:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">let</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span>
<span class="token keyword">let</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> <span class="token punctuation">{</span> a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> callback <span class="token punctuation">}</span> <span class="token operator">=</span> req<span class="token punctuation">.</span>query
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 1</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 2</span>
  <span class="token comment">// 注意哦，返回给script标签，浏览器直接把这部分字符串执行</span>
  res<span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>callback<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">('数据包')</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">)</span>
</code></pre></div><p>前端这样简单地调用一下就好了:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token function">jsonp</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  url<span class="token operator">:</span> <span class="token string">'http://localhost:3000'</span><span class="token punctuation">,</span>
  params<span class="token operator">:</span> <span class="token punctuation">{</span> 
    a<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
    b<span class="token operator">:</span> <span class="token number">2</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">data</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">// 拿到数据进行处理</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 数据包</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><p>和CORS相比，JSONP 最大的优势在于兼容性好，IE 低版本不能使用 CORS 但可以使用 JSONP，缺点也很明显，请求方法单一，只支持 GET 请求。</p> <h2 id="nginx"><a href="#nginx" class="header-anchor">#</a> Nginx</h2> <p>Nginx 是一种高性能的反向代理服务器，可以用来轻松解决跨域问题。</p> <p>what？反向代理？我给你看一张图你就懂了。
<a data-fancybox="" title="result" href="/images/html-015.png"><img src="/images/html-015.png" alt="result"></a></p> <p>正向代理帮助客户端访问客户端自己访问不到的服务器，然后将结果返回给客户端。
反向代理拿到客户端的请求，将请求转发给其他的服务器，主要的场景是维持服务器集群的负载均衡，换句话说，反向代理帮其它的服务器拿到请求，然后选择一个合适的服务器，将请求转交给它。
因此，两者的区别就很明显了，正向代理服务器是帮客户端做事情，而反向代理服务器是帮其它的服务器做事情。
好了，那 Nginx 是如何来解决跨域的呢？
<a href="http://xn--client-vy7ir11boygk0bw0htzh41xky4alwycqjl1svi6e.com" target="_blank" rel="noopener noreferrer">比如说现在客户端的域名为client.com<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>，<a href="http://xn--server-vy7ip41avjdj6h4rej83dt93b.com" target="_blank" rel="noopener noreferrer">服务器的域名为server.com<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>，客户端向服务器发送 Ajax 请求，当然会跨域了，那这个时候让 Nginx 登场了，通过下面这个配置:</p> <div class="language- extra-class"><pre class="language-text"><code>server {
  listen  80;
  server_name  client.com;
  location /api {
    proxy_pass server.com;
  }
}
</code></pre></div><p>Nginx 相当于起了一个跳板机，<a href="http://xn--client-ov7i8n853bojjv54bpvcvzab25m6p8demg.com" target="_blank" rel="noopener noreferrer">这个跳板机的域名也是client.com<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>，让客户端首先访问 <a href="http://client.com/api%EF%BC%8C%E8%BF%99%E5%BD%93%E7%84%B6%E6%B2%A1%E6%9C%89%E8%B7%A8%E5%9F%9F%EF%BC%8C%E7%84%B6%E5%90%8E" target="_blank" rel="noopener noreferrer">client.com/api，这当然没有跨域，然后<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a> Nginx 服务器作为反向代理，<a href="http://xn--server-245jf65cyo8a032bkf4aqxi.com" target="_blank" rel="noopener noreferrer">将请求转发给server.com<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>，当响应返回时又将响应给到客户端，这就完成整个跨域请求的过程。</p> <p>其实还有一些不太常用的方式，大家了解即可，比如postMessage，当然WebSocket也是一种方式，但是已经不属于 HTTP 的范畴，另外一些奇技淫巧就不建议大家去死记硬背了，一方面从来不用，名字都难得记住，另一方面临时背下来，面试官也不会对你印象加分，因为看得出来是背的。当然没有背并不代表减分，把跨域原理和前面三种主要的跨域方式理解清楚，经得起更深一步的推敲，反而会让别人觉得你是一个靠谱的人。</p> <h4 id="参考资料"><a href="#参考资料" class="header-anchor">#</a> 参考资料</h4> <p><a href="https://juejin.im/post/5e76bd516fb9a07cce750746" target="_blank" rel="noopener noreferrer">聊一聊 cookie<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a><br></p></div> <footer class="page-edit"><!----> <!----></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/http/010.html" class="prev">
        10 Cookie
      </a></span> <span class="next"><a href="/http/012.html">
        12 Http2
      </a>
      →
    </span></p></div> </main></div><div class="global-ui"></div></div>
    <script src="/assets/js/app.e358a08d.js" defer></script><script src="/assets/js/2.88fa18d1.js" defer></script><script src="/assets/js/33.cac3e2f0.js" defer></script>
  </body>
</html>
